home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Converters / Convert_PICT / Source / RegionConverter.m < prev    next >
Text File  |  1995-06-12  |  45KB  |  1,210 lines

  1. /***********************************************************************\
  2. region converter for Convert PICT which converts graphics from PICT to eps formats.
  3. Copyright (C) 1993 David John Burrowes
  4.  
  5. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version.
  6.  
  7. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  8.  
  9. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  10.  
  11. The author, David John Burrowes, can be reached at:
  12.     davidjohn@kira.net.netcom.com
  13.     David John Burrowes
  14.     1926 Ivy #10
  15.     San Mateo, CA 94403-1367
  16. \***********************************************************************/
  17.  
  18. #import "PICTFile.h"
  19. #import "PSFile.h"
  20. #import <stdio.h>
  21. #import <stdlib.h>
  22.  
  23. #import  "RegionConverter.h"
  24.  
  25. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  26. //    This class is dedicated to just converting PICT regions.  Really, I suppose it is
  27. //    part of the PICTConverter class, but is just separated for orderliness regions.
  28. //    You really should read the .rtf file for this class, though, because I give a dense
  29. //    little description of the algorithm used here in english.  Some terms and the like
  30. //    are defined there are are used here without any definition.
  31. //    Note: CleanUp contains a known memory leak.
  32. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  33.  
  34.  
  35. @implementation regionConverter
  36.  
  37. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  38. //    Routine:        init
  39. //    Parameters:    none
  40. //    Returns:        self
  41. //    Stores:        none
  42. //    Description:
  43. //        This just initializes all the instance variables.
  44. //    Bugs:
  45. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  46. - init
  47. {
  48.  
  49.     ActiveList = EndOfActiveList;
  50.     RegionData = EndOfGroups;
  51.     ShapeStorage = NoShape;
  52.     boundsRect = NULL;
  53.     
  54.     return self;
  55. }
  56.  
  57.  
  58. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  59. //    Routine:        ConvertRegionFrom:To:
  60. //    Parameters:    a pict and a ps file object
  61. //    Returns:        self
  62. //    Stores:        error codes
  63. //    Description:
  64. //        This method serves as the govenor of the conversion of PICT Region to PS
  65. //        data.  It does little save for calling other methods, and assuring that all went
  66. //        well with each.
  67. //    Bugs:
  68. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  69. - ConvertRegionFrom: PictFile  To: PsFile
  70. {
  71.     SegmentAddr            theSegment;
  72.     SegmentGroupAddr    firstGroup;
  73.     [self   ResetResults];
  74.     //
  75.     //    Read in the region.  If nothing went wrong, then proceed.
  76.     //
  77.     [self   ReadInRegionFrom: PictFile];
  78.     if ([self   GetErrorCode] >= ERR_OK)    // Note a place where a 'severity' would be good
  79.     {
  80.         //
  81.         //    If there are groups (segments on a line), then process them.
  82.         //
  83.         if (RegionData != EndOfGroups)
  84.         {
  85.             //
  86.             //    Prime the conversion by adding all the lines from th first group
  87.             //    to the set of started shapes (which also adds them to the active list).
  88.             //    We do this because they're going to end up there anyway.  And, by doing
  89.             //    it now, we save on time, and potentially allow ConvertRegionData
  90.             //    to avoid having to deal with initial-case data.
  91.             //
  92.             firstGroup = RegionData;
  93.             theSegment = [self   GetNextSegmentFromGroup: firstGroup];
  94.             while (theSegment != NullSegment)
  95.             {
  96.                 [self   AddShapeFrom: theSegment->left To: theSegment->right
  97.                     At: firstGroup->y];
  98.                 free (theSegment);
  99.                 theSegment = [self   GetNextSegmentFromGroup: firstGroup];
  100.             }
  101.             //
  102.             //    With the active list now set up, proceeed to convert any others.
  103.             //
  104.             [self   ConvertRegionData];
  105.         }
  106.         //
  107.         //    Finally, write out whatever data we have converted.
  108.         //
  109.         [self  WriteTo: PsFile];
  110.         [self   CleanUp];
  111.     }
  112.     return self;
  113. }
  114.  
  115.  
  116. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  117. //    Routine:        ReadInRegionFrom:
  118. //    Parameters:    a pict file instance
  119. //    Returns:        self
  120. //    Stores:        error codes
  121. //    Description:
  122. //        This performs the relatively mundane task of reading in a pict region and
  123. //        storing it in 0 or more groups in this instance.  When this finishes, the
  124. //        current position in the pict file will be immediately after the last 0x7FFF in the
  125. //        pict file, and the instance variable regionData will contain a full set of group
  126. //        structures describing the Region.
  127. //        For details about the format of both regions and the groups, see the
  128. //        documentation for this class.
  129. //    Bugs:
  130. //        This routine basically always works.  That is, it never detects a condition
  131. //        so bad that it thinks it should stop.  This is, of course, bad because such an
  132. //        occasion would well occurr.
  133. //        I've seemingly arbitrarily decided to support the case where the x coordinates of
  134. //        a segment are reversed.  I serously doubt taht this would happen, and perhaps it
  135. //        is really the indication of a serious error.  But, since  there's no spec on how regions are
  136. //        to behave, I'm assuming as leniant a stance as I can.
  137. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  138. - ReadInRegionFrom: PictFile
  139. {
  140.     Integer    RegionSize;
  141.     Integer    y;
  142.     Integer    x1;
  143.     Integer    x2;
  144.     SegmentGroupAddr    Group        = EndOfGroups;
  145.     SegmentGroupAddr    newGroup    = EndOfGroups;
  146.     PositiveInteger        bytesConsumed;
  147.     //
  148.     //    Read in the first 10 bytes (size and bounding rect).  Store the bounding rect.
  149.     //    These should always be here.
  150.     //
  151.     RegionSize = [PictFile   GetINTEGER];
  152.     boundsRect = [PictFile   GetRect];
  153.     //
  154.     //    If the region is only 10 bytes long, then we're done (this is probably the most common
  155.     //    region type that one will see).
  156.     //
  157.     if (RegionSize == 10)
  158.         [self   StoreErrorCode: ERR_OK AndText: "Read Region OK"];
  159.     else if (RegionSize < 10)
  160.     {
  161.         //
  162.         //    If the region size is less than 10, it seems likely that the region is
  163.         //    corrupted, and we should abort.
  164.         //
  165.         [self   StoreErrorCode: ERR_BADDATA
  166.                 AndText: "Region declared to be < 10 bytes.  Possible data corruption?"];
  167.     }
  168.     else
  169.     {
  170.         bytesConsumed = 10;
  171.         //
  172.         //    The region is made up of more than just a bounding rectangle, so start
  173.         //    reading in the data.  Stop when our Y value is declared to be 32767 or when
  174.         //    we've reached the number of bytes in a region (these should be simultaneous)
  175.         //
  176.         y = [PictFile   GetINTEGER];
  177.         bytesConsumed +=2;
  178.         while  ( (y != EndOfRegion)  ||  (bytesConsumed != RegionSize))
  179.         {
  180.             //
  181.             //    Each set of segments at 1 y value get stored in a group.  Create the new
  182.             //    group and add it to the end of the current group chain.  Link it into
  183.             //    the instance variable RegionData if it is the first.  In any case, point Group,
  184.             //    the pointer to the current group being added to, to this new group.
  185.             //
  186.             newGroup = (SegmentGroupAddr) malloc(sizeof(SegmentGroup));
  187.             newGroup->y = y;
  188.             newGroup->segmentData = EndOfChain;
  189.             newGroup->next = EndOfGroups;
  190.             if (RegionData == EndOfGroups)
  191.                 RegionData = newGroup;
  192.             else
  193.                 Group->next =  newGroup;
  194.             Group = newGroup;
  195.             //
  196.             //    Read in the pairs of x coordinate data until we find an end of region line.
  197.             //    Merge each segment with the current group as we go.
  198.             //
  199.             x1 = [PictFile   GetINTEGER];
  200.             bytesConsumed +=2;
  201.             while (x1 != EndOfRegionLine)
  202.             {
  203.                 x2 = [PictFile   GetINTEGER];
  204.                 //
  205.                 //    Store the segment in the current group.  Deal with the unlikely
  206.                 //    case the x1 and x2 values might be reversed for our needs.
  207.                 //
  208.                 if (x1 < x2)
  209.                     [self   AddSegmentFrom: x1  To: x2 ToGroup: Group];
  210.                 else
  211.                 {
  212.                     [self   AddSegmentFrom: x2  To: x1 ToGroup: Group];
  213.                     [self   StoreErrorCode: ERR_WEIRDDATA
  214.                         AndText: "x1 and x2 were reversed in region data!"];
  215.                 }
  216.                 x1 = [PictFile   GetINTEGER];
  217.                 bytesConsumed += 4;
  218.             }
  219.             y = [PictFile   GetINTEGER];
  220.             bytesConsumed +=2;
  221.         }
  222.         //
  223.         //    If if the last number read wasn't 7FFF and we've reached the end of the
  224.         //    region, or vice versa, set an error.
  225.         //
  226.         if ( y != EndOfRegion)
  227.             [self   StoreErrorCode: ERR_BADDATA
  228.                 AndText: "We did not find the end of the region!!  File may not be a PICT file!"];
  229.         else if ( bytesConsumed  != RegionSize)
  230.             [self   StoreErrorCode: ERR_BADSIZE
  231.                 AndText: "End of region found prematurely.  Is file corrupted?"];
  232.     }
  233.     return self;
  234. }
  235.  
  236.  
  237. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  238. //    Routine:        AddSegmentFrom:To:ToGroup:
  239. //    Parameters:    A left x and a right x coordinate and a group
  240. //    Returns:        self
  241. //    Stores:        none
  242. //    Description:
  243. //        Given the bounds of a segment, add a new segment to the chain of segments in the
  244. //        specified group.
  245. //    Bugs:
  246. //    History:
  247. //        93.11.07    djb    Fixed a subtle problem.  If there was 1 segment in group, and the one
  248. //                    we were adding belonged on the end of the chain, we were nevertheless
  249. //                    adding it first.  oops.
  250. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  251.  
  252. -AddSegmentFrom: (Integer) left  To: (Integer) right  ToGroup: (SegmentGroupAddr) group;
  253. {
  254.     SegmentLinkAddr    location;
  255.     SegmentLinkAddr    newSegment;
  256.     //
  257.     //    First, create the Segment link that we're going to add
  258.     //
  259.     newSegment = (SegmentLinkAddr) malloc(sizeof(SegmentLink));
  260.     newSegment->left = left;
  261.     newSegment->right = right;
  262.     //
  263.     //    If this is the first segment, then point the global to it
  264.     //
  265.     if (group->segmentData == EndOfChain)
  266.     {
  267.         newSegment->nextSegment = EndOfChain;
  268.         group->segmentData = newSegment;
  269.     }
  270.     else
  271.     {
  272.         //
  273.         //    Otherwise, walk down the chain, and look for a nice place to add
  274.         //    the segment.  A 'nice place' means a location at the end of the chain, OR, to the
  275.         //    left of a segment that starts with a larger x coordinate in left.  In this way,
  276.         //    we try to keep the segments sorted by starting x coordinate.
  277.         //
  278.         location = group->segmentData;
  279.         while ( (location->nextSegment != EndOfChain) &&
  280.                     (location->nextSegment->left < newSegment->left) )
  281.             location = location->nextSegment;
  282.         //
  283.         //    If we actually go as the first segment, stick us in.  Otherwise,
  284.         //    stick us before the segment we have found to have a greater value than the
  285.         //    the our left x value 
  286.         //
  287.         if (location == group->segmentData)
  288.         {
  289.             if (location->left < newSegment->left)
  290.             {
  291.                 newSegment->nextSegment = location->nextSegment;
  292.                 location->nextSegment = newSegment;
  293.             }
  294.             else
  295.             {
  296.                 newSegment->nextSegment = group->segmentData;
  297.                 group->segmentData = newSegment;
  298.             }
  299.         }
  300.         else
  301.         {
  302.             newSegment->nextSegment = location->nextSegment;
  303.             location->nextSegment = newSegment;
  304.         }
  305.     }
  306.     return self;
  307. }
  308.  
  309.  
  310. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  311. //    Routine:        GetNextSegmentFromGroup:
  312. //    Parameters:    A segment group
  313. //    Returns:        the address of a segment that has been removed from the group
  314. //    Stores:        error codes
  315. //    Description:
  316. //        This removes the next segment from thegroup, and returns it.  If there
  317. //        are no remaining segments, and error is stored, and a NullSegmentAddress
  318. //        is returned.
  319. //    Bugs:
  320. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  321.  
  322. - (SegmentAddr) GetNextSegmentFromGroup: (SegmentGroupAddr) group
  323. {
  324.     SegmentAddr            newSegment;
  325.     SegmentLinkAddr    firstSegment = group->segmentData;
  326.  
  327.     if (firstSegment == EndOfChain)
  328.     {
  329.         newSegment = NullSegment;
  330.         [self   StoreErrorCode: ERR_NOSEGMENTS
  331.                 AndText: "There are no more segments in this group!"];
  332.     }
  333.     else
  334.     {
  335.         //
  336.         //    Create a segment to be returned, and fill it in.  Remove the current segment.
  337.         //
  338.         newSegment = (SegmentAddr) malloc(sizeof(Segment));
  339.         newSegment->left = firstSegment->left;
  340.         newSegment->right = firstSegment->right;
  341.         group->segmentData = firstSegment->nextSegment;
  342.         free(firstSegment);
  343.         [self   StoreErrorCode: ERR_OK AndText: "Got the segment."];
  344.     }
  345.  
  346.     return newSegment;
  347. }
  348.  
  349.  
  350. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  351. //    Routine:        AddShapeFrom:To:At:
  352. //    Parameters:    The left and right coordinates of the first segment of the shape, and their
  353. //                y coordinate.
  354. //    Returns:        self
  355. //    Stores:        none
  356. //    Description:
  357. //        This adds a new shape to the linked list of shapes stored so far.
  358. //        Since this list isn't sorted, we just stick the shape in at thefirst place we find.
  359. //    Bugs:
  360. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  361. - AddShapeFrom: (Integer) left  To: (Integer) right  At: (Integer) y
  362. {
  363.     ShapeAddr            newShape    = (ShapeAddr) malloc(sizeof(Shape));
  364.     LineSegmentAddr        newLine        = (LineSegmentAddr) malloc(sizeof(LineSegment));
  365.     //
  366.     //    Fill in the line and shape structures
  367.     //
  368.     newLine->leftNext = NoLine;
  369.     newLine->rightNext = NoLine;
  370.     newLine->left.x = left;
  371.     newLine->left.y = y;
  372.     newLine->right.x = right;
  373.     newLine->right.y = y;
  374.     newLine->used = NO;
  375.     newShape-> theLine = newLine;
  376.     //
  377.     //    Store the shapes in the linked list (we don't need to worry about whether
  378.     //    there was anything in there already)
  379.     //
  380.     newShape->nextShape = ShapeStorage;
  381.     ShapeStorage = newShape;
  382.     //
  383.     //    As a curtesy, add an Active segment as well, since a new shape with one line
  384.     //    automatically has an active segment coresponding to that line.
  385.     //
  386.     [self   AddActiveFrom: left To: right WithLeft: newLine  AndRight: newLine];
  387.     return self;
  388. }
  389.  
  390.  
  391. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  392. //    Routine:        AddAntiShapeFrom:To:At:
  393. //    Parameters:    The left and right coordinates of the first segment of the shape
  394. //    Returns:        the newly created line
  395. //    Stores:        none
  396. //    Description:
  397. //        This adds a new shape to the linked list of shapes stored so far.
  398. //        This is interesting in that we do NOT add an active area.  This is because a shape
  399. //        of this variety is appearing within another, and it's 'area' is actually the lack of
  400. //        an area...
  401. //    Bugs:
  402. //    History:
  403. //        93.11.07    djb    AT LAST!!  Found that I wasn't seting the used 'flag' to NO which
  404. //                    was causing seemingly random damage to the regions when they
  405. //                    were written out.
  406. //                    Also changed it so anti shapes are appended to the end of the list
  407. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  408. - (LineSegmentAddr)  AddAntiShapeFrom: (Integer) left  To: (Integer) right  At: (Integer) y
  409. {
  410.     ShapeAddr            newShape    = (ShapeAddr) malloc(sizeof(Shape));
  411.     LineSegmentAddr        newLine        = (LineSegmentAddr) malloc(sizeof(LineSegment));
  412.     ShapeAddr            tempShape;
  413.     //
  414.     //    Fill in the line and shape structures
  415.     //
  416.     newLine->leftNext = NoLine;
  417.     newLine->rightNext = NoLine;
  418.     newLine->left.x = left;
  419.     newLine->left.y = y;
  420.     newLine->right.x = right;
  421.     newLine->right.y = y;
  422.     newLine->used = NO;
  423.     newShape-> theLine = newLine;
  424.     //
  425.     //    Store the shapes in the linked list.  We store them at the end of the list.  Why?
  426.     //    Sometimes an antishape is merely the underside of an existing shape.  At the end
  427.     //    of the list, they'll be visited by the writeout routine last, and so if they were such,
  428.     //    they'll already have been makred as being used.  If this isn't done, the underside
  429.     //    of the region may be written out before it's upper side, and this can cause strange
  430.     //    effects when framing because the framing code may assume that any new shape
  431.     //    starts with its top segment.
  432.     //
  433.     if (ShapeStorage == NoShape)
  434.     {
  435.         ShapeStorage = newShape;
  436.         newShape->nextShape = NoShape;
  437.     }
  438.     else
  439.     {
  440.         tempShape = ShapeStorage;
  441.         while (tempShape->nextShape != NoShape)
  442.             tempShape = tempShape->nextShape;
  443.         newShape->nextShape = NoShape;
  444.         tempShape->nextShape = newShape;
  445.     }
  446.     return newLine;
  447. }
  448.  
  449.  
  450. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  451. //    Routine:        AddLineFrom:To:At:WithLeft:AndRight:
  452. //    Parameters:    The left and right coordinates of the first segment of the shape,
  453. //                plus the pointers to the line(s) that this one is 'adjacent' to.
  454. //    Returns:        the new line segment
  455. //    Stores:        none
  456. //    Description:
  457. //        Given the specified data, build a new line segment, and and connect it to
  458. //        other lines in a shape.  See documentation for this class for more info.
  459. //    Bugs:
  460. //        If one asks to add a line that has neither left nor right neighbors, then this
  461. //        will duitfully not link this to other segments, and it's entirely possible that
  462. //        trouble will result.  More simply: Make sure this always called with at least
  463. //        one neighboring line segment.
  464. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  465. - (LineSegmentAddr) AddLineFrom: (Integer) left  To: (Integer) right  At: (Integer) y
  466.     WithLeft: (LineSegmentAddr) leftline  AndRight: (LineSegmentAddr) rightline
  467. {
  468.     LineSegmentAddr        newLine        = (LineSegmentAddr) malloc(sizeof(LineSegment));
  469.     //
  470.     //    Fill in the line and shape structures
  471.     //
  472.     newLine->leftNext = leftline;
  473.     newLine->rightNext = rightline;
  474.     newLine->left.x = left;
  475.     newLine->left.y = y;
  476.     newLine->right.x = right;
  477.     newLine->right.y = y;
  478.     newLine->used = NO;
  479.     //
  480.     //    Link the other line(s) to this line.  Compare our left and right coordinates
  481.     //    with those of the line(s) we are connected to.  Our left end should be
  482.     //    connected to a line with the same x coordinate on one of its ends (either its
  483.     //    left or right end could be attached to our end).
  484.     //
  485.     if (leftline != NoLine)
  486.     {
  487.         if (leftline->left.x == left)
  488.             leftline->leftNext = newLine;
  489.         else
  490.             leftline->rightNext = newLine;
  491.     }
  492.     if (rightline != NoLine)
  493.     {
  494.         if (rightline->right.x == right)
  495.             rightline->rightNext = newLine;
  496.         else
  497.             rightline->leftNext = newLine;
  498.     }
  499.  
  500.     return newLine;
  501. }
  502.  
  503.  
  504. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  505. //    Routine:        AddActiveFrom:To:WithLeft:AndRight:
  506. //    Parameters:    A segment
  507. //    Returns:        self
  508. //    Stores:        none
  509. //    Description:
  510. //        Given a segment, add it to the set of active segments.
  511. //    Bugs:
  512. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  513.  
  514. - AddActiveFrom: (Integer) left  To: (Integer) right  WithLeft:
  515.         (LineSegmentAddr) leftline  AndRight: (LineSegmentAddr) rightline
  516. {
  517.     ActiveSegmentAddr    location;
  518.     ActiveSegmentAddr    previous;
  519.     ActiveSegmentAddr    newSegment;
  520.     //
  521.     //    First, create the Segment link that we're going to add
  522.     //
  523.     newSegment = (ActiveSegmentAddr) malloc(sizeof(ActiveSegment));
  524.     newSegment->left = left;
  525.     newSegment->right = right;
  526.     newSegment->leftLine = leftline;
  527.     newSegment->rightLine = rightline;
  528.     //
  529.     //    If this is the first segment, then point the global to it
  530.     //
  531.     if (ActiveList == EndOfActiveList)
  532.     {
  533.         newSegment->next = EndOfActiveList;
  534.         newSegment->previous = EndOfActiveList;
  535.         ActiveList = newSegment;
  536.     }
  537.     else
  538.     {
  539.         //
  540.         //    Otherwiese, walk down the list, and look for a nice place
  541.         //    to add the new one.  A 'nice place' means a location at the end of the chain,
  542.         //    OR, to the left of a segment that starts with a larger x coordinate in left.
  543.         //    In this way, we try to keep the segments sorted by starting x coordinate.
  544.         //
  545.         location = ActiveList;
  546.         previous = EndOfActiveList;
  547.         while ( (location != EndOfActiveList) &&
  548.             (location->left < newSegment->left) )
  549.         {
  550.             previous = location;
  551.             location = location->next;
  552.         }
  553.         if (location == ActiveList)
  554.         {
  555.             newSegment->previous = EndOfActiveList;
  556.             newSegment->next = location;
  557.             location->previous = newSegment;
  558.             ActiveList = newSegment;
  559.         }
  560.         else
  561.         {
  562.             newSegment->previous = previous;
  563.             newSegment->next = previous->next;
  564.             previous->next = newSegment;
  565.             if (location != EndOfActiveList)
  566.                 location->previous = newSegment;
  567.         }
  568.     }
  569.     return self;
  570. }
  571.  
  572.  
  573. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  574. //    Routine:        RemoveActive:
  575. //    Parameters:    A segment
  576. //    Returns:        self
  577. //    Stores:        none
  578. //    Description:
  579. //        Given an active segment, link it's previous and following active segments
  580. //        together, and then nuke the active segment.  (it's for the ease of removing an
  581. //        active that these structures are doubly linked)
  582. //    Bugs:
  583. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  584.  
  585. - RemoveActive: (ActiveSegmentAddr) theActive
  586. {
  587.     if (theActive->previous == EndOfActiveList)
  588.         ActiveList = theActive->next;
  589.     else
  590.         theActive->previous->next = theActive->next;
  591.  
  592.     if (theActive->next != EndOfActiveList)
  593.         theActive->next->previous = theActive->previous;
  594.  
  595.     free(theActive);
  596.     return self;
  597. }
  598.  
  599.  
  600.  
  601. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  602. //    Routine:        FindFirstActiveLeftIn:To:
  603. //    Parameters:    A first and last x coordinate
  604. //    Returns:        The address of an active segment
  605. //    Stores:        none
  606. //    Description:
  607. //        This walks through the active segment list, and looks for an active whose left
  608. //        boundrary is in (left,right].  If it finds such a beastie, it returns the address of
  609. //        that active, otherwise it returns EndOfActiveList.
  610. //    Bugs:
  611. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  612.  
  613. - (ActiveSegmentAddr) FindFirstActiveLeftIn: (Integer) left  To: (Integer) right
  614. {
  615.     ActiveSegmentAddr    location;
  616.     Boolean                found = NO;
  617.  
  618.     location = ActiveList;
  619.  
  620.     while ( (location != EndOfActiveList) && (found != YES))
  621.     {
  622.         if ( (location->left > left) && (location->left <= right) )
  623.             found = YES;
  624.         else
  625.             location = location->next;
  626.     }
  627.  
  628.     return location;
  629. }
  630.  
  631.  
  632. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  633. //    Routine:        ConvertRegionData
  634. //    Parameters:    none
  635. //    Returns:        self
  636. //    Stores:        error codes
  637. //    Description:
  638. //        This assumes that the first group of segments has been dealt with, and so it
  639. //        walks through the remaining data in the rows.
  640. //        Basics of algorithm: Take a segment from the current group.  Walk down the
  641. //        active list until we find one with some kinda overlap with the segment.
  642. //        modify all things accordingly, and move on to the next segment (If the segment
  643. //        extended to the right of the matching active, we modify it and send it through
  644. //        for continued processing).  If a line never overlaps with
  645. //        any of the actives, it falls out of the inner loop with an overlap of None, and is
  646. //        added as the start of a new shape.
  647. //    Bugs:
  648. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  649.  
  650. - ConvertRegionData
  651. {
  652.     SegmentGroupAddr    currentGroup;
  653.     Integer                currentY;
  654.     SegmentAddr            curSeg;
  655.     OverlapKind            overlap;
  656.     ActiveSegmentAddr    Active, temp;
  657.     ActiveSegmentAddr    first;
  658.     Integer                firstX;
  659.     LineSegmentAddr        newline;
  660.     Boolean                DoMore    = NO;    // do more loops to continue prossessing the segment
  661.     //
  662.     //    Set currentGroup to point to the second group.
  663.     //
  664.     currentGroup = RegionData->next;
  665.     while (currentGroup != EndOfGroups)
  666.     {
  667.         currentY = currentGroup->y;
  668.         curSeg = [self   GetNextSegmentFromGroup: currentGroup];
  669.         while (curSeg != NullSegment)
  670.         {
  671.             overlap = None;    // default overlap to none in case no actives presently.
  672.             Active = ActiveList;
  673.             while (Active != EndOfActiveList)
  674.             {
  675.                 DoMore = NO;
  676.                 overlap = [self   CheckHowSegment: curSeg   Intersects: Active];
  677.                 switch (overlap)
  678.                 {
  679.                     //
  680.                     //    These are all the ways in which the line segment can overlap with
  681.                     //    an active segment.  I have diagrams for each which I can't
  682.                     //    include here which make the operations make much more sense.
  683.                     //    A line segment may in fact overlap an arbitrary number of active segments,
  684.                     //    but we only examine one at a time, and then after having dealt with
  685.                     //    the intersections, pass what remains of the segment for more.
  686.                     //
  687.                     case None :
  688.                         break;
  689.                     case Equal :
  690.                         [self   AddLineFrom:  curSeg->left To: curSeg->right At: currentY
  691.                             WithLeft: Active->leftLine  AndRight: Active->rightLine];
  692.                         break;
  693.                     case Larger:
  694.                         [self   AddLineFrom:  Active->left  To: Active->right At: currentY
  695.                             WithLeft: Active->leftLine  AndRight: Active->rightLine];
  696.                         [self   AddShapeFrom: curSeg->left To: Active->left At: currentY];
  697.                         //
  698.                         //    Since the right part might intersect with other actives,
  699.                         //    just store it as a segment, and we'll process it shortly
  700.                         //
  701.                         curSeg->left = Active->right;
  702.                         DoMore = YES;
  703.                         break;
  704.                     case Within:
  705.                         newline = [self   AddAntiShapeFrom:  curSeg->left
  706.                             To: curSeg->right  At: currentY];
  707.                         [self   AddActiveFrom: Active->left   To: curSeg->left
  708.                             WithLeft: Active->leftLine  AndRight: newline];
  709.                         [self   AddActiveFrom: curSeg->right   To: Active->right
  710.                             WithLeft: newline  AndRight: Active->rightLine];
  711.                         break;
  712.                     case TouchOuterLeft:
  713.                         newline = [self   AddLineFrom:  curSeg->left  To: curSeg->right
  714.                             At: currentY  WithLeft:NoLine AndRight: Active->leftLine];
  715.                         [self   AddActiveFrom: curSeg->left   To: Active->right
  716.                             WithLeft: newline  AndRight: Active->rightLine];
  717.                         break;
  718.                     case TouchOuterRight:
  719.                         first = [self   FindFirstActiveLeftIn: curSeg->left To: curSeg->right];
  720.                         if (first != EndOfActiveList)
  721.                             firstX = first->left;
  722.                         else
  723.                             firstX = 100000;  // definitely beyond any curSeg->right.
  724.                         if (firstX < curSeg->right)
  725.                         {
  726.                             newline = [self   AddLineFrom:  curSeg->left  To: firstX
  727.                                 At: currentY  WithLeft: Active->rightLine
  728.                                 AndRight: NoLine];
  729.                             [self   AddActiveFrom: Active->left   To: firstX
  730.                                 WithLeft: Active->leftLine  AndRight: newline];
  731.                             curSeg->left = firstX;
  732.                             DoMore = YES;
  733.                         }
  734.                         else if (firstX == curSeg->right)
  735.                         {
  736.                             newline = [self   AddLineFrom:  curSeg->left  To: curSeg->right
  737.                                 At: currentY  WithLeft: Active->rightLine
  738.                                 AndRight: first->leftLine];
  739.                             [self   AddActiveFrom: Active->left   To: first->right
  740.                                 WithLeft: Active->leftLine  AndRight: first->rightLine];
  741.                             [self   RemoveActive: first];
  742.                         }
  743.                         else // firstX > curSeg->right  // segment is totally 'ours'
  744.                         {
  745.                             newline = [self   AddLineFrom:  curSeg->left  To: curSeg->right
  746.                                 At: currentY  WithLeft: Active->rightLine
  747.                                 AndRight: NoLine];
  748.                             [self   AddActiveFrom: Active->left   To: curSeg->right
  749.                                 WithLeft: Active->leftLine  AndRight: newline];
  750.                         }
  751.                         break;
  752.                     case TouchInnerLeft:
  753.                         newline = [self   AddLineFrom:  curSeg->left  To: curSeg->right
  754.                             At: currentY WithLeft: Active->leftLine AndRight: NoLine];
  755.                         [self   AddActiveFrom: curSeg->right   To: Active->right
  756.                             WithLeft: newline  AndRight: Active->rightLine];
  757.                         break;
  758.                     case TouchInnerRight:
  759.                         newline = [self   AddLineFrom:  curSeg->left  To: curSeg->right
  760.                             At: currentY WithLeft: NoLine  AndRight: Active->rightLine];
  761.                         [self   AddActiveFrom: Active->left   To: curSeg->left
  762.                             WithLeft: Active->leftLine  AndRight: newline];
  763.                         break;
  764.                     case TouchInnerLeftExtendRight:
  765.                         (void)  [self   AddLineFrom:  Active->left  To: Active->right
  766.                             At: currentY WithLeft: Active->leftLine
  767.                             AndRight: Active->rightLine];
  768.                         //
  769.                         //    Modify the current segment, and continue processing.
  770.                         //
  771.                         curSeg->left = Active->right;
  772.                         DoMore = YES;
  773.                         break;
  774.                     case TouchInnerRightExtendLeft:
  775.                         [self   AddLineFrom:  Active->left  To: Active->right At:currentY
  776.                             WithLeft: Active->leftLine  AndRight: Active->rightLine];
  777.                         [self   AddShapeFrom: curSeg->left To: Active->left At: currentY];
  778.                         break;
  779.                     case LeftOverlap:
  780.                         newline = [self   AddLineFrom:  Active->left  To: curSeg->right
  781.                             At: currentY WithLeft: Active->leftLine  AndRight: NoLine];
  782.                         [self AddActiveFrom: curSeg->right To:  Active->right
  783.                             WithLeft: newline  AndRight: Active->rightLine];
  784.                         [self   AddShapeFrom: curSeg->left To: Active->left At: currentY];
  785.                         break;
  786.                     case RightOverlap:
  787.                         newline = [self   AddLineFrom:  curSeg->left  To: Active->right
  788.                             At: currentY WithLeft: NoLine  AndRight: Active->rightLine];
  789.                         [self AddActiveFrom: Active->left To:  curSeg->left
  790.                             WithLeft: Active->leftLine  AndRight: newline];
  791.                         //
  792.                         //    Modify the current segment, and continue processing.
  793.                         //
  794.                         curSeg->left = Active->right;
  795.                         DoMore = YES;
  796.                         break;
  797.                 }
  798.                 if ( (overlap == None)  || (DoMore == YES) )
  799.                 {
  800.                     temp = Active;
  801.                     Active = Active->next;
  802.                     if (DoMore == YES)
  803.                         [self   RemoveActive: temp];
  804.                 }
  805.                 else 
  806.                 {
  807.                     [self   RemoveActive: Active];
  808.                     Active = EndOfActiveList;  // force processing of next segment
  809.                 }
  810.             }
  811.             if ( (overlap == None)  || (DoMore == YES) )
  812.                 [self   AddShapeFrom: curSeg->left To: curSeg->right At: currentY];
  813.             free(curSeg);
  814.             curSeg = [self   GetNextSegmentFromGroup: currentGroup];
  815.         }
  816.         currentGroup = currentGroup->next;
  817.     }
  818.     return self;
  819. }
  820.  
  821.  
  822. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  823. //    Routine:        CheckHowSegment:Intersects:
  824. //    Parameters:    A segment and an active segment
  825. //    Returns:        an enumerated value indicating what kind of intersection there is
  826. //    Stores:        none
  827. //    Description:
  828. //        This takes two segments, and figures out what kind of intersection they have.
  829. //        The kinds of intersection are all named relative to the active segment
  830. //        (i.eTouchInnerRight says that the segment touches the inner right edge of
  831. //        the active segment (the left of the segment touches the right of the active, and
  832. //        the rest of it is completely within the active's span).  ).  There are 13 cases
  833. //        to consider.
  834. //    Bugs:
  835. //        This should be rtf code, so I could include illustrations of what these mean.
  836. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  837.  
  838. - (OverlapKind) CheckHowSegment: (SegmentAddr) segment
  839.                         Intersects: (ActiveSegmentAddr) active;
  840. {
  841.     OverlapKind        kind;
  842.  
  843.  
  844.     if (segment->left > active->left)
  845.     {
  846.         if (segment->left > active->right)
  847.             kind = None;
  848.         else if (segment->left == active->right)
  849.             kind = TouchOuterRight;
  850.         else if (segment->right > active->right)
  851.             kind = RightOverlap;
  852.         else if (segment->right == active->right)
  853.             kind = TouchInnerRight;
  854.         else // (segment->right < active->right)
  855.             kind = Within;
  856.     }
  857.     else if (segment->left == active->left)
  858.     {
  859.         if (segment->right > active->right)
  860.                 kind = TouchInnerLeftExtendRight;
  861.         else if (segment->right == active->right)
  862.             kind = Equal;
  863.         else // (segment->right < active->right)
  864.             kind = TouchInnerLeft;
  865.     }
  866.     else // (segment->left < active->left)
  867.     {
  868.         if (segment->right > active->left)
  869.         {
  870.             if (segment->right > active->right)
  871.                 kind = Larger;
  872.             else if (segment->right == active->right)
  873.                 kind = TouchInnerRightExtendLeft;
  874.             else // if (segment->right < active->right)
  875.                 kind = LeftOverlap;
  876.         }
  877.         else if (segment->right == active->left)
  878.             kind = TouchOuterLeft;
  879.         else // (segment->right < active->left)
  880.             kind = None;
  881.     }
  882.  
  883.     return kind;
  884. }
  885.  
  886.  
  887.  
  888.  
  889. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  890. //    Method:        WriteInteger:
  891. //    Parameters:
  892. //        An integer, and the file to write it to
  893. //    Returns:     self
  894. //    Stores:        none  explicitly (subcalls may)
  895. //    Description:
  896. //        This simply writes out an integer with a trailing space.  ooh ahh. 
  897. //    Bugs:
  898. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  899. - WriteInteger: (Integer) theInt To: theFile
  900. {
  901.     CString    temp    = NewCString(12);
  902.     
  903.     [theFile   WriteTextUsing: temp WithFormat: "%d ", theInt];
  904.     
  905.     FreeCString(temp);
  906.     return self;
  907. }
  908.  
  909.  
  910.  
  911. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  912. //    Method:        WriteShapeFromLine:
  913. //    Parameters:    A line to start writing a shape from
  914. //    Returns:     self
  915. //    Stores:        none
  916. //    Description:
  917. //        Walk through the shape, and print out its coordinates as we go.
  918. //        As we visit each line segment, we mark it as used so we kno when to stop
  919. //        when we eventually circle around the shape of the shape.
  920. //        When we are in a line, we can only go in the left direction, or the right, and we
  921. //        we figure that out based on the direction we were going in the previous.
  922. //        (note: the direction dictates the order in which we print the line's coordinates,
  923. //        as well as which edge to go from there).  When preparing to go to another line,
  924. //        we check: is the line one can reach from this next line's left side the same as the current line?
  925. //        if so, then we'll be entering on the left side of the line, and will have to go right.
  926. //        otherwise, we'll be going left.  So, we set the direction flag accordingly, and proceed until we
  927. //        find a null line, or we find a line marked as used. (This happens to work properly when
  928. //        one is dealing with a square where each line segment poins to the other).
  929. //        It's also relevant to note that this data structure only stores horizontal lines, like
  930. //        a region, kinda.  Vertical lines are implied by the pointers between the horozontals.
  931. //        And, of course, there's no such thing as a diagonal line here.
  932. //    Bugs:
  933. //        I'd hoped to deal with cases where the shape is incomplete by dropping edges
  934. //        to QuickDraw infinity (32767).  This proved to be a bit more work than I wanted
  935. //        to bother with, though.  So, I simply drop out, and don't do anything.
  936. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  937.  
  938. - WriteShapeFromLine:  (LineSegmentAddr) startLine  To: PsFile
  939. {
  940.     LineSegmentAddr        theLine        = startLine;
  941.     DirectionType        direction        = toRight;
  942.     Integer                lineCount    = 0;
  943.     Integer                numOnLine    = 0;
  944.  
  945.     while (theLine->used != YES)
  946.     {
  947.         //
  948.         //    Tag the line as used, both so we don't loop around in it forever, as well
  949.         //    as to keep us inadvertantly printing it twice, since it is possible for
  950.         //    the converter to think it's identified two+ shapes when it's really identified
  951.         //    two+ parts of one shape
  952.         //
  953.         theLine->used = YES;
  954.  
  955.         if (direction == toLeft)
  956.         {
  957.             [self   WriteInteger:  theLine->right.x To: PsFile];
  958.             [self   WriteInteger:  theLine->right.y To: PsFile];
  959.             [PsFile   WriteText: " "];
  960.             [self   WriteInteger:  theLine->left.x To: PsFile];
  961.             [self   WriteInteger:  theLine->left.y To: PsFile];
  962.             if (theLine->leftNext == NoLine)
  963.             {
  964.                 //    Do nothing.  This line will be noted as being used in the
  965.                 //    While loop condition, and we will drop out.
  966.             }
  967.             else
  968.             {
  969.                 if (theLine->leftNext->leftNext == theLine)
  970.                     direction = toRight;
  971.                 else
  972.                     direction = toLeft;
  973.                 theLine = theLine->leftNext;
  974.             }
  975.         }
  976.         else
  977.         {
  978.             [self   WriteInteger:  theLine->left.x To: PsFile];
  979.             [self   WriteInteger:  theLine->left.y To: PsFile];
  980.             [PsFile   WriteText: " "];
  981.             [self   WriteInteger:  theLine->right.x To: PsFile];
  982.             [self   WriteInteger:  theLine->right.y To: PsFile];
  983.             if (theLine->rightNext == NoLine)
  984.             {
  985.                 //    Do nothing.  This line will be noted as being used in the
  986.                 //    While loop condition, and we will drop out.
  987.             }
  988.             else
  989.             {
  990.                 if (theLine->rightNext->rightNext == theLine)
  991.                     direction = toLeft;
  992.                 else
  993.                     direction = toRight;
  994.                 theLine = theLine->rightNext;
  995.             }
  996.         }
  997.         lineCount++;
  998.         numOnLine++;
  999.         //
  1000.         //    If we've written a certain number of points on the output line, then
  1001.         //    move on to the next outputline, otherwise separate from the next by a
  1002.         //    couple spaces and continue.
  1003.         //
  1004.         if (numOnLine >= 5)
  1005.         {
  1006.             [PsFile   ForceNewLine];
  1007.             numOnLine = 0;
  1008.         }
  1009.         else
  1010.             [PsFile   WriteText: "  "];
  1011.     }
  1012.     if (numOnLine != 0)
  1013.         [PsFile   ForceNewLine];
  1014.     [self   WriteInteger:  (lineCount * 2) To: PsFile]; // write out # of points we wrote
  1015.     [PsFile   ForceNewLine];
  1016.     return self;
  1017. }
  1018.  
  1019.  
  1020. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1021. //    Method:        WriteTo:
  1022. //    Parameters:    The file object to write to
  1023. //    Returns:     self
  1024. //    Stores:        none  explicitly (subcalls may)
  1025. //    Description:
  1026. //        This walks through the chain of shapes.  If the first line of a shape has not
  1027. //        been used (written out), we assume that the shape hasn't been touched, and ask to
  1028. //        write it out.  Otherwise, we skip it and go on to the next shape.
  1029. //        We assume that WriteShapeFrom: terminates its work with a newline, and
  1030. //        we follow this with the total number of shapes we wrote, and the rectangle
  1031. //        that bound the whole region.
  1032. //    Bugs:
  1033. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1034. - WriteTo: PsFile
  1035. {
  1036.     LineSegmentAddr        theLine;
  1037.     Integer                numShapes        = 0;
  1038.     ShapeAddr            currentShape    = ShapeStorage;
  1039.     
  1040.     while (currentShape != NoShape)
  1041.     {
  1042.         theLine = currentShape->theLine;
  1043.         if (theLine->used == NO)
  1044.         {
  1045.             //
  1046.             //    Write out the points that make up the shape
  1047.             //
  1048.             numShapes++;
  1049.             [self   WriteShapeFromLine: theLine  To: PsFile];
  1050.         }
  1051.         currentShape = currentShape->nextShape;
  1052.     }
  1053.     [self   WriteInteger:  numShapes To: PsFile];
  1054.     [PsFile   ForceNewLine];
  1055.     //
  1056.     //    Write the bounding rectangle
  1057.     //
  1058.     [self   WriteInteger: boundsRect->top To: PsFile];
  1059.     [self   WriteInteger: boundsRect->left To: PsFile];
  1060.     [self   WriteInteger: boundsRect->bottom To: PsFile];
  1061.     [self   WriteInteger: boundsRect->right To: PsFile];
  1062.     [PsFile   ForceNewLine];
  1063.  
  1064.     return self;
  1065. }
  1066.  
  1067.  
  1068.  
  1069. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1070. //    Routine:        CleanUp:
  1071. //    Parameters:    none
  1072. //    Returns:        self
  1073. //    Stores:        none
  1074. //    Description:
  1075. //        this walks through the instance variables, and frees up all the chains of data.
  1076. //        hopefully.
  1077. //    Bugs:
  1078. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1079. - CleanUp
  1080. {
  1081.     SegmentGroupAddr    tempGroup    = RegionData;
  1082.     SegmentGroupAddr    scratch1;
  1083.     SegmentLinkAddr    tempSegmentLink, scratch2;
  1084.     ActiveSegmentAddr    scratch3,  tempActiveLink = ActiveList;
  1085.     ShapeAddr            scratch4, currentShape = ShapeStorage;
  1086.     //
  1087.     //    First, purge the data left over from reading in the region
  1088.     //
  1089.     while (tempGroup != EndOfGroups)
  1090.     {
  1091.         tempSegmentLink = tempGroup->segmentData;
  1092.         while (tempSegmentLink != EndOfChain)
  1093.         {
  1094.             scratch2 = tempSegmentLink;
  1095.             tempSegmentLink = tempSegmentLink->nextSegment;
  1096.             free(scratch2);
  1097.         }
  1098.         scratch1 = tempGroup;
  1099.         tempGroup = tempGroup->next;
  1100.         free(scratch1);
  1101.     }
  1102.     RegionData = EndOfGroups;
  1103.     //
  1104.     //    Clear the active list  (it should already be cleared... but just in case...
  1105.     //
  1106.     while (tempActiveLink != EndOfActiveList)
  1107.     {
  1108.         scratch3 = tempActiveLink;
  1109.         tempActiveLink = tempActiveLink->next;
  1110.         free(scratch3);
  1111.     }
  1112.     ActiveList = EndOfActiveList;
  1113.     //
  1114.     //    Clear the shapes.
  1115.     //
  1116.     while (currentShape != NoShape)
  1117.     {
  1118.         //
  1119.         //    SERIOUS memory leak problem here.  We do not free the individual lines that
  1120.         //    make up the shape here.  Why not?  Because a line could be pointed to by more than
  1121.         //    one shape, and it is going to be difficult to deal with this, since it means that for every
  1122.         //    line we free from a shape, we must first check whether it is pointed to by any of the
  1123.         //    remaining shapes.  It's not presently worth the effort, given the very low usage this
  1124.         //    will get.
  1125.         //
  1126.         scratch4 = currentShape;
  1127.         currentShape = currentShape->nextShape;
  1128.         free(scratch4);
  1129.     }
  1130.     ShapeStorage =  NoShape;
  1131.     //
  1132.     //    Write out the bounding rectangle
  1133.     //
  1134.     if (boundsRect != NULL)
  1135.         free(boundsRect);
  1136.     boundsRect = NULL;
  1137.     return self;
  1138. }
  1139.  
  1140.  
  1141.  
  1142. @end
  1143.  
  1144.  
  1145.  
  1146. #if 0
  1147. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1148. //    Method:        FindEndFrom:Going:
  1149. //    Parameters:    A line to start walking from, and a direction to go in.
  1150. //    Returns:     The direction, on the final line, that the last coordinate is
  1151. //    Stores:        The final line in first_result
  1152. //    Description:
  1153. //        The purpose of this is to start at a specific line, and continue until we find
  1154. //        a line that leasds to (a) a Null line poiner, or (b) a line marked as used.
  1155. //        We use a traversal algorithm also used by writeShapeFromLine: that keeps us
  1156. //        from walking back the way we came inadvertantly.  When we finish, we return
  1157. //        a pointer to the final good line we were on, and a direction (toLeft or toRight)
  1158. //        indicating which way were heading when we found the end (hence, if we
  1159. //        return toRight, the right coordinate in the line we return is the 'last' coordinate
  1160. //        in the shape in this direction.
  1161. //    Bugs:
  1162. //        This routine isn't actually used for anything.  It's included here merely
  1163. //        because I wrote it to start to deal with the 'null line' problem in WriteShapeFromLine:
  1164. //        but ended up abandoning the project as being too much work for such a tiny
  1165. //        special case.  It remains in case I should ever choose to deal with that case.
  1166. //    NOTE:
  1167. //        Please read RegionConverter.rtf, since that describes much more about how
  1168. //        this object behaves!
  1169. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1170. -(DirectionType) FindEndFrom: (LineSegmentAddr) startLine  Going: (DirectionType) theDir
  1171. {
  1172.     LineSegmentAddr        theLine        = startLine;
  1173.     DirectionType        direction        = theDir;
  1174.     Boolean                endFound    = NO;
  1175.  
  1176.     while (endFound == NO)
  1177.     {
  1178.         if (direction == toLeft)
  1179.         {
  1180.             if ( (theLine->leftNext == NoLine)  || (theLine->leftNext->used  == YES) )
  1181.                 endFound = YES;
  1182.             else
  1183.             {
  1184.                 if (theLine->leftNext->leftNext == theLine)
  1185.                     direction = toRight;
  1186.                 else
  1187.                     direction = toLeft;
  1188.                 theLine = theLine->leftNext;
  1189.             }
  1190.         }
  1191.         else
  1192.         {
  1193.             if ( (theLine->rightNext == NoLine)  || (theLine->rightNext->used  == YES) )
  1194.                 endFound = YES;
  1195.             else
  1196.             {
  1197.                 if (theLine->rightNext->rightNext == theLine)
  1198.                     direction = toLeft;
  1199.                 else
  1200.                     direction = toRight;
  1201.                 theLine = theLine->rightNext;
  1202.             }
  1203.         }
  1204.     }
  1205.     [self   StorePointer: (Pointer) theLine];
  1206.  
  1207.     return direction;
  1208. }
  1209. #endif
  1210.